home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / SWAG / SWAGA_C / COMM.SWG / 0075_Basic RS232 Unit.pas < prev    next >
Pascal/Delphi Source File  |  1995-02-28  |  10KB  |  318 lines

  1.  
  2. {$I-} { I/O hecking OFF }
  3. {$R-} { Range checking OFF }
  4. {$S-} { Stack checking OFF }
  5. {$V-} { Var-str checking OFF}
  6. {$B+} {Boolean complete evaluation on}
  7. {$N-} {No numeric coprocessor}
  8.  
  9. Unit RS232INT;
  10.  
  11. Interface
  12.  
  13. Uses
  14.   Crt,
  15.   Dos;
  16.  
  17. { global declarations  for Async}
  18.  
  19. type
  20.   astr = String[160];  { generic string type for parameters      }
  21.  
  22. const
  23.   buffer_max = 5120;
  24.  
  25. var
  26.   Async_OriginalVector : pointer;
  27.   buffer       : Array[0..buffer_max] of char;
  28.  
  29.   Async_Open_Flag    : Boolean;   { true if Open but no Close }
  30.   Async_Port         : Integer;   { current Open port number (1 or 2) }
  31.   base               : Integer;   { base for current open port }
  32.   Async_Irq          : Integer;   { irq for current open port }
  33.  
  34.   Async_Buffer_Overflow : Boolean;  { True if buffer overflow has happened }
  35.   Async_Buffer_Used     : Integer;
  36.   Async_MaxBufferUsed   : Integer;
  37.  
  38.                  { buffer is empty if Head = Tail }
  39.   Buffer_head  : Integer;   { Locn in buffer to put next char }
  40.   Buffer_tail  : Integer;   { Locn in buffer to get next char }
  41.   Buffer_newtail : Integer;
  42.  
  43.  
  44. { End of Async declarations }
  45.  
  46. procedure async_isr; INTERRUPT;
  47. procedure set_baud(r:integer);
  48. function charin:char;
  49. procedure charout(c:char);
  50. function cdet:boolean;
  51. procedure Async_Init;
  52. procedure Async_Close;
  53. Procedure Async_Open(ComPort       : Integer;
  54.                     BaudRate      : Integer;
  55.                     Parity        : Char;
  56.                     WordSize      : Integer;
  57.                     StopBits      : Integer);
  58.  
  59. Implementation
  60.  
  61. const
  62.   UART_THR = $00;
  63.   UART_RBR = $00;
  64.   UART_IER = $01;
  65.   UART_IIR = $02;
  66.   UART_LCR = $03;
  67.   UART_MCR = $04;
  68.   UART_LSR = $05;
  69.   UART_MSR = $06;
  70.  
  71.   I8088_IMR = $21;   { port address of the Interrupt Mask Register }
  72.  
  73.  
  74. var
  75.  
  76.   Async_BIOS_Port_Table : Array[1..4] of Integer absolute $40:0;
  77.  
  78. const
  79.   Async_Num_Bauds = 8;
  80.   Async_Baud_Table : array [1..Async_Num_Bauds] of record
  81.                                                      Baud, Bits : integer
  82.                                                    end
  83.                    = ((Baud:110;  Bits:$00),
  84.                       (Baud:150;  Bits:$20),
  85.                       (Baud:300;  Bits:$40),
  86.                       (Baud:600;  Bits:$60),
  87.                       (Baud:1200; Bits:$80),
  88.                       (Baud:2400; Bits:$A0),
  89.                       (Baud:4800; Bits:$C0),
  90.                       (Baud:9600; Bits:$E0));
  91.  
  92.  
  93. PROCEDURE DisableInterrupts; inline($FA {cli} );     {MACROS}
  94. PROCEDURE EnableInterrupts;  inline($FB {sti} );
  95.  
  96. procedure BIOS_RS232_Init(ComPort, ComParm : Integer);
  97. var
  98.   Regs : registers;
  99. begin
  100.   with Regs do
  101.     begin
  102.       ax := ComParm and $00FF;  { AH=0; AL=ComParm }
  103.       dx := ComPort;
  104.       Intr($14, Regs)
  105.     end
  106. end;
  107.  
  108. procedure Async_Isr;  {INTERRUPT;}
  109. begin
  110.   Inline(
  111.     $FB/                           { STI }
  112.       { get the incomming character }
  113.       { buffer[Buffer_head] := Chr(Port[UART_RBR + base]); }
  114.     $8B/$16/base/                  { MOV DX,base }
  115.     $EC/                           { IN AL,DX }
  116.     $8B/$1E/Buffer_head/           { MOV BX,Buffer_head }
  117.     $88/$87/buffer/                { MOV buffer[BX],AL }
  118.       { Async_Buffer_NewHead := Buffer_head + 1; }
  119.     $43/                           { INC BX }
  120.       { if Async_Buffer_NewHead > buffer_max then
  121.           Async_Buffer_NewHead := 0; }
  122.     $81/$FB/buffer_max/            { CMP BX,buffer_max }
  123.     $7E/$02/                       { JLE L001 }
  124.     $33/$DB/                       { XOR BX,BX }
  125.       { if Async_Buffer_NewHead = Buffer_tail then
  126.           Async_Buffer_Overflow := TRUE
  127.         else }
  128. {L001:}
  129.     $3B/$1E/Buffer_tail/     { CMP BX,Buffer_tail }
  130.     $75/$08/                       { JNE L002 }
  131.     $C6/$06/Async_Buffer_Overflow/$01/ { MOV Async_Buffer_Overflow,1 }
  132.     $90/                           { NOP generated by assembler for some reason }
  133.     $EB/$16/                       { JMP SHORT L003 }
  134.       { begin
  135.           Buffer_head := Async_Buffer_NewHead;
  136.           Async_Buffer_Used := Async_Buffer_Used + 1;
  137.           if Async_Buffer_Used > Async_MaxBufferUsed then
  138.             Async_MaxBufferUsed := Async_Buffer_Used
  139.         end; }
  140. {L002:}
  141.     $89/$1E/Buffer_head/           { MOV Buffer_head,BX }
  142.     $FF/$06/Async_Buffer_Used/     { INC Async_Buffer_Used }
  143.     $8B/$1E/Async_Buffer_Used/     { MOV BX,Async_Buffer_Used }
  144.     $3B/$1E/Async_MaxBufferUsed/   { CMP BX,Async_MaxBufferUsed }
  145.     $7E/$04/                       { JLE L003 }
  146.     $89/$1E/Async_MaxBufferUsed/   { MOV Async_MaxBufferUsed,BX }
  147. {L003:}
  148.       { disable interrupts }
  149.     $FA/                           { CLI }
  150.       { Port[$20] := $20; }        { use non-specific EOI }
  151.     $B0/$20/                       { MOV AL,20h }
  152.     $E6/$20                        { OUT 20h,AL }
  153.        )
  154. end; { Async_Isr }
  155.  
  156. procedure Async_Init;
  157. begin
  158.   Async_Open_Flag := FALSE;
  159.   Async_Buffer_Overflow := FALSE;
  160.   Async_Buffer_Used := 0;
  161.   Async_MaxBufferUsed := 0;
  162. end; { Async_Init }
  163.  
  164. procedure Async_Close;
  165. var
  166.   i, m : Integer;
  167. begin
  168.   if Async_Open_Flag then
  169.     begin
  170.  
  171.       { disable the IRQ on the 8259 }
  172.       DisableInterrupts;
  173.       i := Port[I8088_IMR];        { get the interrupt mask register }
  174.       m := 1 shl Async_Irq;        { set mask to turn off interrupt }
  175.       Port[I8088_IMR] := i or m;
  176.  
  177.       { disable the 8250 data ready interrupt }
  178.       Port[UART_IER + base] := 0;
  179.  
  180.       { disable OUT2 on the 8250 }
  181.       Port[UART_MCR + base] := 0;
  182.       EnableInterrupts;
  183.  
  184.       SetIntVec(Async_Irq + 8,Async_OriginalVector);
  185.  
  186.       { re-initialize our data areas so we know the port is closed }
  187.       Async_Open_Flag := FALSE
  188.  
  189.     end
  190. end; { Async_Close }
  191.  
  192. Procedure Async_Open(ComPort       : Integer;
  193.                     BaudRate      : Integer;
  194.                     Parity        : Char;
  195.                     WordSize      : Integer;
  196.                     StopBits      : Integer);
  197. { open a communications port }
  198. var
  199.   ComParm : Integer;
  200.   i, m : Integer;
  201. begin
  202.   if Async_Open_Flag then Async_Close;
  203.  
  204.   if (ComPort = 4) and (Async_BIOS_Port_Table[4] <> 0) then
  205.     Async_Port := 4;
  206.   if (ComPort = 3) and (Async_BIOS_Port_Table[3] <> 0) then
  207.     Async_Port := 3; 
  208.   if (ComPort = 2) and (Async_BIOS_Port_Table[2] <> 0) then
  209.     Async_Port := 2;
  210.   if (ComPort = 1) and (Async_BIOS_Port_Table[1] <> 0) then
  211.     Async_Port := 1;  { default to COM1 }
  212.   base := Async_BIOS_Port_Table[Async_Port];
  213.   Async_Irq := Hi(base) + 1;
  214.  
  215.   if (Port[UART_IIR + base] and $00F8)=0
  216.   then
  217.     begin
  218.       Buffer_head := 0;
  219.       Buffer_tail := 0;
  220.       Async_Buffer_Overflow := FALSE;
  221.  
  222.   { Build the ComParm for RS232_Init }
  223.   { See Technical Reference Manual for description }
  224.  
  225.       ComParm := $0000;
  226.  
  227.   { Set up the bits for the baud rate }
  228.       i := 0;
  229.       repeat
  230.         i := i + 1
  231.       until (Async_Baud_Table[i].Baud = BaudRate) or (i = Async_Num_Bauds);
  232.       ComParm := ComParm or Async_Baud_Table[i].Bits;
  233.  
  234.       if Parity in ['E', 'e'] then ComParm := ComParm or $0018
  235.       else if Parity in ['O', 'o'] then ComParm := ComParm or $0008
  236.       else ComParm := ComParm or $0000;  { default to No parity }
  237.  
  238.       if WordSize = 7 then ComParm := ComParm or $0002
  239.       else ComParm := ComParm or $0003;  { default to 8 data bits }
  240.  
  241.       if StopBits = 2 then ComParm := ComParm or $0004
  242.       else ComParm := ComParm or $0000;  { default to 1 stop bit }
  243.  
  244.   { use the BIOS COM port initialization routine to save typing the code }
  245.       BIOS_RS232_Init(Async_Port - 1, ComParm);
  246.  
  247.       GetIntVec(Async_Irq + 8, Async_OriginalVector);
  248.       SetIntVec(Async_Irq + 8, @Async_Isr);
  249.  
  250.   { read the RBR and reset any possible pending error conditions }
  251.   { first turn off the Divisor Access Latch Bit to allow access to RBR, etc. }
  252.  
  253.       DisableInterrupts;
  254.  
  255.       Port[UART_LCR + base] := Port[UART_LCR + base] and $7F;
  256.   { read the Line Status Register to reset any errors it indicates }
  257.       i := Port[UART_LSR + base];
  258.   { read the Receiver Buffer Register in case it contains a character }
  259.       i := Port[UART_RBR + base];
  260.  
  261.   { enable the irq on the 8259 controller }
  262.       i := Port[I8088_IMR];  { get the interrupt mask register }
  263.       m := (1 shl Async_Irq) xor $00FF;
  264.       Port[I8088_IMR] := i and m;
  265.  
  266.   { enable the data ready interrupt on the 8250 }
  267.       Port[UART_IER + base] := $01; { enable data ready interrupt }
  268.  
  269.   { enable OUT2 on 8250 }
  270.       i := Port[UART_MCR + base];
  271.       Port[UART_MCR + base] := i or $08;
  272.  
  273.       EnableInterrupts;
  274.       Async_Open_Flag := TRUE;
  275.       {Async_Open := TRUE}
  276.     end
  277. end; { Async_Open }
  278.  
  279. procedure set_baud(r:integer);
  280. var rl:real; a:byte;
  281. begin
  282.   if (r>=300) and (r<=9600) then begin
  283.     rl:=115200.0/r;
  284.     r:=trunc(rl);
  285.     a:=port[3+base] or 128;
  286.     port[base+3]:=a;
  287.     port[base]:=lo(r);
  288.     port[1+base]:=hi(r);
  289.     port[3+base]:=a and 127;
  290.   end;
  291. end;
  292.  
  293. function charin:char;
  294. var t:char;
  295. begin
  296.   if buffer_Head = buffer_Tail Then
  297.     t:=#0
  298.   else begin
  299.     disableinterrupts;
  300.     t:=buffer[buffer_Tail];
  301.     buffer_Tail:=(buffer_Tail+1) mod (buffer_max+1);
  302.     enableinterrupts;
  303.   end;
  304.  charin := t;
  305. end;
  306.  
  307. procedure charout(c:char);
  308. begin
  309.   while (port[base+5] and 32)=0 do;
  310.   port[base]:=ord(c);
  311. end;
  312.  
  313. function cdet:boolean;
  314. begin
  315.   cdet:=(port[base+6] and 128)<>0;
  316. end;
  317. end.
  318.